using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Reflection;
using Utilities.Common;

namespace DataBaseManager
{
    public enum eAction { eaInsert, eaInsertWithId, eaUpdate, eaAutoDetect}
    public delegate void DataBind<T>(T item, DataRow dr);
    internal class DataBaseHelper
    {
        public static U Load<U, T>(DataTable dt, DataBind<T> bind) where U : List<T>, new() where T : new()
        {
            U coleccion = new U();
            T item = new T();
            
            ClassInfo clase = ClassInfoCache.GetClassInfo(item);         
            PropInfoManager properties = clase.Properties;
           
            foreach (DataRow dr in dt.Rows)
            {
                item = new T();

                foreach (PropInfo p in properties)
                {
                    p.SetValue(item,dr);                
                }
                //para cada item llamo el delegado
                if (bind != null) bind(item, dr);
                coleccion.Add(item);
            }
            return coleccion;
        }

        public static U Load<U, T>(DataTable dt) where U : List<T>, new() where T : new()
        {
            return Load<U, T>(dt, null);
        }


        internal static void Save(object obj, Database database)
        {
            Save(obj, database, new ParameterManager(), eAction.eaAutoDetect);           
        }

        internal static void Save(object obj, Database database, eAction action)
        {
            Save(obj, database, new ParameterManager(), action);
        }   

        internal static void Save(object obj, Database database, ParameterManager extraParameters, eAction action)
        {            
            ClassInfo clase = ClassInfoCache.GetClassInfo(obj);
            TableName table = new TableName (clase.TableName.Name);
            ParameterManager parameters = BuildParameters(clase.Properties,obj);
#warning habria q revisar q no los duplique, por ahora queda asi
            parameters.AddRange(extraParameters);
            object id = 0;

            switch (action)
            {
                case eAction.eaInsertWithId:
                    id = database.InsertWithId(table, parameters);
                    clase.Properties.PropId.SetValue(obj, id);
                    break;
                case eAction.eaInsert:
                    database.Insert(table, parameters);
                    break;
                case eAction.eaUpdate:
                    database.Update(table, parameters);
                    break;
                case eAction.eaAutoDetect:
                    if (clase.Properties.PropId.IsEmpty(obj))//si no tiene id...
                    {
                        id = database.InsertWithId(table, parameters);
                        clase.Properties.PropId.SetValue(obj, id);
                    }
                    else
                    {
                        database.Update(table, parameters);
                    }
                    break;
                default:
                    break;
            }


        }

        internal static ParameterManager BuildParameters(PropInfoManager propInfoManager, object obj)
        {
            ParameterManager parameters = new ParameterManager();
            propInfoManager.FindAll(p=>!p.NotSaveMark).ForEach(p => parameters.Add(BuildParameter(p, obj)));
            return parameters;

        }

        internal static Parameter BuildParameter(PropInfo p, object obj)
        {
            //cuando armo el parametro, si es un objeto (IsClass=true), me devuelve el id del objeto
            //sino, el valor directamente
            return new Parameter(p.Parent.FieldPrefix + p.Name, p.GetValue(obj, p.IsClass), p.IsId);

        }


    }


}
